package com.houger.ddd.redpacket.facade.impl;

import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.houger.ddd.redpacket.application.GrabRedPacketAdaptor;
import com.houger.ddd.redpacket.application.RedPacketAdaptor;
import com.houger.ddd.redpacket.facade.dto.GrabRedPacketDto;
import com.houger.ddd.redpacket.facade.dto.RedPacketDto;
import com.houger.ddd.redpacket.domain.GrabRedPacketDomain;
import com.houger.ddd.redpacket.domain.RedPacketDomain;
import com.houger.ddd.redpacket.facade.RedPacketFacade;
import com.houger.ddd.redpacket.infra.entity.GrabRedPacket;
import com.houger.ddd.redpacket.infra.entity.RedPacket;
import com.houger.ddd.redpacket.infra.mq.RedPackerKafkaProducer;
import com.houger.ddd.redpacket.infra.mysql.GrabRedPacketRepository;
import com.houger.ddd.redpacket.infra.mysql.RedPacketRepository;
import com.houger.ddd.redpacket.infra.redis.RedisRepository;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Houger.
 * @date 2024年04月18日 15:52.
 */
@Slf4j
@Service
public class RedPacketFacadeImpl implements RedPacketFacade {
    @Resource
    private RedisRepository redisRepository;
    @Resource
    private RedPacketRepository redPacketRepository;

    @Resource
    private GrabRedPacketRepository grabRedPacketRepository;

    @Resource
    private RedPackerKafkaProducer redPackerKafkaProducer;

    public void saveRedPacketToCache(RedPacketDomain domain) {
        String res = redisRepository.initRedPacket(domain);
        log.info("res=" + res);
        if (!"ok".equals(res)) throw new RuntimeException("发红包redis异常");
    }


    @Override
    public void saveRedPacketToDb(final RedPacketDomain domain) {
        try {
            redPacketRepository.save(RedPacketAdaptor.toEntity(domain));
        } catch (Exception e) {
            log.error("saveRedPacketToDb err,domain=[{}],e={}", domain.getRedPacketId(), e.getMessage());
        }

    }


    @Override
    public Integer grab(final GrabRedPacketDomain domain) {

        return redisRepository.grab(domain);
    }

    @Override
    public Integer sendMq(final GrabRedPacketDomain domain) {
        try {
            redPackerKafkaProducer.send(domain);
        } catch (Exception e) {
            return -1;

        }
        return 200;
    }

    @Override
    public void saveGrabToDb(final GrabRedPacketDomain domain) {
        saveGrabRedPacket(domain);
    }

    @Async
    public void saveGrabRedPacket(final GrabRedPacketDomain domain) {
        final GrabRedPacket save = grabRedPacketRepository.save(GrabRedPacketAdaptor.toEntity(domain));
        log.info("saveGrabRedPacket id={}", save.getId());
    }

    @Async
    @Override
    public void updateResidual(final String redPacketId, final String money) {
        String burnoutTime = "";
        burnoutTime = redisRepository.getBurnoutTime(redPacketId);
        final RedPacket redPacket = redPacketRepository.findByRedPacketId(redPacketId);
        final BigDecimal residualAmount = new BigDecimal(redPacket.getResidualAmount());

        final Long residualCount = Long.parseLong(redPacket.getResidualCount()) - 1L;
        final String residualAmountTo = residualAmount.subtract(new BigDecimal(money)).toString();
        if(StringUtils.isNotBlank(burnoutTime)){

            int i = redPacketRepository.updateResidualAndBurnoutTime(redPacket.getId(),
                residualAmount.toString(),
                residualAmountTo,
                String.valueOf(residualCount),
                burnoutTime
            );
            return;
        }
        int i = redPacketRepository.updateResidual(redPacket.getId(),
            residualAmount.toString(),
            residualAmountTo,
            String.valueOf(residualCount)
        );
    }


    @Override
    public List<GrabRedPacketDto> getGrabRedPacketList(final String redPacketId) {
        List<GrabRedPacketDto> list = new ArrayList<>();
        final Map<Object, Object> grabRedPacketMap = redisRepository.getGrabRedPacketInfoList(redPacketId);
        if (MapUtil.isNotEmpty(grabRedPacketMap)) {
            for (Map.Entry<Object, Object> entry : grabRedPacketMap.entrySet()) {
                String value = (String) entry.getValue();
                final GrabRedPacketDto dto = JSON.parseObject(value, GrabRedPacketDto.class);
                dto.setUserId((String) entry.getKey());
                dto.setRedPacketId(redPacketId);
                list.add(dto);
            }
            return list;
        }

        //查db
        List<GrabRedPacket> grabRedPacketList = grabRedPacketRepository.findAllByRedPacketId(redPacketId);
        return grabRedPacketList.stream().map(g -> {
            final GrabRedPacketDto grabRedPacketDto = new GrabRedPacketDto();
            grabRedPacketDto.setRedPacketId(g.getRedPacketId());
            grabRedPacketDto.setUserId(g.getUserId());
            grabRedPacketDto.setMoney(g.getMoney());
            grabRedPacketDto.setGrabTime(g.getGrabTime());
            grabRedPacketDto.setBestLuck(g.getBestLuck());
            return grabRedPacketDto;
        }).collect(Collectors.toList());

        // 放入缓存 TODO
//        final Object collect = grabRedPacketList.stream().map(e -> {
//            return BeanUtil.copyProperties(e, GrabRedPacketDomain.class);
//        }).collect((k, v) -> {
//            final Map<String, String> map1 = new HashMap<>();
//            map1.put(k, v);
//        });
    }

    @Override
    public RedPacketDto getRedPacketInfo(final String redPacketId) {
        final Map<Object, Object> map = redisRepository.getRedPacketByCache(redPacketId);
        if (MapUtil.isNotEmpty(map)) {
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.convertValue(map, RedPacketDto.class);
        }

        // 缓存没有，查db 再写缓存
        RedPacket entity = redPacketRepository.findByRedPacketId(redPacketId);
        redisRepository.saveByDb(entity);
        return Optional.ofNullable(entity)
            .map(RedPacketAdaptor::entityToDto)
            .orElseGet(RedPacketDto::new);
    }

}